// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author: jhkim, lucasyoo, junglekim

#pragma once

#include <fmt/core.h>
#include <glog/logging.h>
#include <google/protobuf/util/json_util.h>
#include <nlohmann/json.hpp>
#include <rapidjson/encodedstream.h>
#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>

#include "coin2/app/driver.h"
#include "coin2/exchange/base/feed/update.h"
#include "coin2/exchange/base/feed/timer/timer.h"
#include "coin2/exchange/base/feed/timer/deadline_timer.h"
#include "coin2/strategy/hooks/pnl_monitor.h"
#include "coin2/strategy/strategy.h"
#include "coin2/strategy/cmm_strat/cmm_pricer.h"
#include "coin2/strategy/cmm_strat/cmm_order_controller.h"
#include "coin2/strategy/util/reporter2.h"


namespace coin2::strategy::cmm_strat {

using coin2::app::impl::Driver;
using coin2::exchange::base::feed::FeedTimer;
using coin2::exchange::base::feed::FeedUpdate;
using coin2::exchange::base::feed::timer::DeadlineTimer;
using coin2::exchange::base::order::OrderUpdate;
using coin2::strategy::util::StrategyReporter2;
using coin2::strategy::hooks::PnlMonitor;

uint64_t GetProductId(const IProduct& product);

class CmmSubStrategy {
 public:
  CmmSubStrategy(const ::nlohmann::json& sub_strat_config, bool dry_run, std::optional<int64_t> ts);

  void GenerateOrderManagePeriod();
  void onFairPriceUpdate(int64_t timestamp);
  void Init(Driver* driver);
  void onBookFeed(const FeedUpdate& upd);
  void onAccountInfo(const OrderUpdate& upd);
  void onAccountOrder(const OrderUpdate& upd);
  // void onHeartbeatFeed(const FeedTimer& timer);
  void onOrderLog(const coin::proto::OrderGatewayLog& order_log);
  void onCleanup(Driver* driver);

  void onOrderManage(int64_t ts);
  void PeriodicOrderManage();

  CmmPricer* Pricer() const { return pricer_.get(); }
  PassiveExecutorConfig ExeConfig0() const { return exe_config0_; }
  PassiveExecutorConfig ExeConfig1() const { return exe_config1_; }
  const bool& IsSameOwner() const { return is_same_owner_; }

 protected:
  nlohmann::json sub_strat_config_;
  std::unique_ptr<CmmPricer> pricer_;
  std::unique_ptr<CmmOrderController> controller_;
  PassiveExecutorConfig exe_config0_;
  PassiveExecutorConfig exe_config1_;
  int64_t strategy_created_time_;
  Driver* driver_;
  std::unique_ptr<boost::asio::deadline_timer> ordermanage_timer_;
  int64_t ordermanage_period_;
  bool is_same_owner_;
};


class CmmStrategy : public coin2::strategy::IStrategy {
 public:
  CmmStrategy(const std::string& strat_json, std::optional<int64_t> ts);

  void Init(Driver* driver) final;
  void onBookFeed(const FeedUpdate& upd) final;
  void onAccountInfo(const OrderUpdate& upd) final;
  void onAccountOrder(const OrderUpdate& upd) final;
  // void onHeartbeatFeed(const FeedTimer& timer) final;
  void onTradeFeed(const FeedUpdate& upd) final {}
  void onOrderLog(const coin::proto::OrderGatewayLog& order_log) final;
  void onCleanup(Driver* driver) final;

 private:
  int64_t strategy_created_time_;
  Driver* driver_;
  nlohmann::json strat_config_;
  std::unique_ptr<StrategyReporter2> strat_reporter_ {nullptr};
  std::unique_ptr<PnlMonitor> pnl_monitor_{nullptr};
  std::unique_ptr<DeadlineTimer> feed_timer_{nullptr};
  std::unordered_map<uint64_t, std::unique_ptr<CmmSubStrategy>> strats_;
  bool dry_report_;
  bool dry_run_;
  std::vector<std::unique_ptr<IProduct>> exclude_products0_;
  std::unordered_map<uint64_t, std::optional<double>> last_exclude_balance0_;
  std::vector<std::unique_ptr<IProduct>> exclude_products1_;
  std::unordered_map<uint64_t, std::optional<double>> last_exclude_balance1_;
  std::unordered_map<uint64_t, std::optional<double>> exclude_midp_;
};



}  // namespace coin2::strategy::cmm_strat
